package com.csthink.web.interfaces;

import com.csthink.breaker.annotations.GuavaLimit;
import com.csthink.breaker.annotations.RedisLimit;
import com.csthink.common.application.ResponseWrapper;
import com.csthink.common.infrastructure.enums.ErrorEnum;
import com.google.common.util.concurrent.RateLimiter;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * 限流控制器测试类
 *
 * @author <a href="mailto:security.2009@live.cn">Mars</a>
 * @since 2023-09-08
 */
@Api(tags = "Test 模块")
@Slf4j
@RestController
@RequestMapping("/limit")
public class LimitTestController {

    /**
     * 限流策略: 1秒钟允许通过2个请求
     */
    private final RateLimiter limiter = RateLimiter.create(2.0);

    private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 基于 Guava RateLimiter 的限流
     * 实际生产中不推荐直接使用，业务代码和限流代码耦合，造成代码冗余
     * 推荐基于 AOP 来实现接口限流
     *
     * @return
     */
    @GetMapping("/test1")
    public ResponseWrapper<String> testLimiter() {
        // 500ms内，如果没有获取到令牌，直接进入服务降级
        boolean tryAcquire = limiter.tryAcquire(500, TimeUnit.MICROSECONDS);

        if (!tryAcquire) {
            log.warn("进入服务降级，时间{}", LocalDateTime.now().format(dtf));
            return ResponseWrapper.error(ErrorEnum.SERVER_BUSY, "当前排队人数较多，请稍后再试!");
        }

        log.info("获取令牌成功，时间{}", LocalDateTime.now().format(dtf));
        return ResponseWrapper.success("请求成功");
    }

    /**
     * 基于 Guava RateLimiter 的 AOP 限流，不支持分布式限流
     *
     * @return
     */
    @GetMapping("/test2")
    @GuavaLimit(key = "limit2", permitsPerSecond = 2, timeout = 500, timeunit = TimeUnit.MILLISECONDS, msg = "系统繁忙，请稍后再试！")
    public ResponseWrapper<String> limit2() {
        log.info("令牌桶limit2获取令牌成功");
        return ResponseWrapper.success("请求成功");
    }

    /**
     * 基于Redis + Lua 的 AOP 限流，支持分布式限流
     */
    @GetMapping("/test3")
    @RedisLimit(key = "redis-limit:test", permitsPerSecond = 3, expire = 1, msg = "当前排队人数较多，请稍后再试！")
    public ResponseWrapper<String> test3() {
        log.info("令牌桶limit3获取令牌成功");
        return ResponseWrapper.success("请求成功");
    }
}
